package com.next.muscu.data;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Observable;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.next.muscu.PersistanceUnitTools;
import com.next.muscu.visual.ModelParamObserver;
import javax.persistence.EntityManager;
//TODO gestion des exceptions de hibernate
/**
 * @author sdelporte
 *
 */
public class ObservableList<T extends MyDataObject> extends Observable implements List<T >, PropertyChangeListener{


	Logger logger = LoggerFactory.getLogger(ObservableList.class);
	private List<T> data = new ArrayList<T>();
	private String query;
	private boolean dataBaseLink = false;
	SessionFactory sessionFactory;
	Session	session; 
	public ObservableList(){};
	public ObservableList(List data)
	{
		this.data = data;
		for(T e : this.data)
		{
			e.addPropertyChangeListener(this);
			
		}
	}
	public ObservableList(String query)
	{
		this.query = query;
		this.dataBaseLink  = true;
		loadData();
		for(T e : this.data)
		{
			e.addPropertyChangeListener(this);
			
		}
	}
	protected void finalize()
	{
		session.close();
	}
	
	public void loadData()
	{
                
                EntityManager em = PersistanceUnitTools.getOneEntityManager();
                List l = em.createQuery(query).getResultList();
                /*sessionFactory = HibernateUtil.getSessionFactory();
		session = sessionFactory.openSession();
		= session.createQuery(query).list();*/
		logger.info(query + " :" + l.size());
		data.addAll(l);
	}
	
	private void persist(T transientInstance) {
		logger.debug("persisting Exo instance");
		try {
			session.persist(transientInstance);
			logger.debug("persist successful");
		} catch (RuntimeException re) {
			logger.error("persist failed", re);
			throw re;
		}
	}
	
	public void add(int index, T element) {
		if(dataBaseLink)
		{
			session.beginTransaction();
			persist(element);
			session.getTransaction().commit();
		}
		data.add(index, element);
		element.addPropertyChangeListener(this);
		ModelParamObserver p = new ModelParamObserver(ModelParamObserver.ADD_SINGLE, index);
		this.notifyObservers(p);
	
	}

	public boolean add(T e) {
		if(dataBaseLink)
		{
			session.beginTransaction();
			persist(e);
		}
		boolean t = data.add(e);
		if(t) {
			if(dataBaseLink) session.getTransaction().commit();	
		e.addPropertyChangeListener(this);
		this.setChanged();
		ModelParamObserver p = new ModelParamObserver(ModelParamObserver.ADD_SINGLE, this.size()-1);
		this.notifyObservers(p);
		}
		else
			if(dataBaseLink) session.getTransaction().rollback();
		return t; 
		
	}

	public boolean addAll(Collection<? extends T> c) {
		if(dataBaseLink)
		{
			session.beginTransaction();
			for(T e:c)
			{
				persist(e);
			}
		}
		boolean t = data.addAll(c);
		int oldEndIndex  = this.size() -1;
		if(t)
		{
			if(dataBaseLink) session.getTransaction().commit();
			for(T e:c)
			{
				e.addPropertyChangeListener(this);
			}
			this.setChanged();
			ModelParamObserver p = new ModelParamObserver(ModelParamObserver.ADD_MULTIPLE, oldEndIndex,this.size()-1);
			this.notifyObservers(p);
		}
		else
			if(dataBaseLink) session.getTransaction().rollback();
		return t;
	}

	public boolean addAll(int index, Collection<? extends T> c) {
		if(dataBaseLink)
		{
			session.beginTransaction();
			for(T e:c)
			{
				persist(e);
			}
		}
		boolean t = data.addAll(index, c);
		if(t)
		{	
			if(dataBaseLink) session.getTransaction().commit();
			for(T e:c)
			{
				e.addPropertyChangeListener(this);
			}
			this.setChanged();
			ModelParamObserver p = new ModelParamObserver(ModelParamObserver.ADD_MULTIPLE, index,index + c.size());
			this.notifyObservers(p);
		}
		else
			if(dataBaseLink) session.getTransaction().rollback();
		return t;
	}

	public void clear() {
		data.clear();
		this.setChanged();
		this.notifyObservers();
	}

	public boolean contains(Object o) {
		return data.contains(o);
	}

	public boolean containsAll(Collection<?> c) {
		return data.containsAll(c);
	}

	public boolean equals(Object o) {
		return data.equals(o);
	}

	public T get(int index) {
		return data.get(index);
	}

	public int hashCode() {
		return data.hashCode();
	}

	public int indexOf(Object o) {
		return data.indexOf(o);
	}

	public boolean isEmpty() {
		return data.isEmpty();
	}

	public Iterator<T> iterator() {
		return data.iterator();
	}

	public int lastIndexOf(Object o) {
		return data.lastIndexOf(o);
	}

	public ListIterator<T> listIterator() {
		return data.listIterator();
	}

	public ListIterator<T> listIterator(int index) {
		return data.listIterator(index);
	}

	public T remove(int index) {
		if(dataBaseLink)
		{
			session.beginTransaction();
			session.delete(data.get(index));
			session.getTransaction().commit();
		}
		T e = data.remove(index);
		e.removePropertyChangeListener(this);
		this.setChanged();
		ModelParamObserver p = new ModelParamObserver(ModelParamObserver.REMOVE_SINGLE,index);
		this.notifyObservers();
		return e;
	}

	synchronized  public boolean remove(Object o) {
		int oldindex = this.indexOf(o);
		if (dataBaseLink)
		{
			session.beginTransaction();
			session.delete(o);
		}
		boolean b = data.remove(o);
		if(b){
			if(dataBaseLink) session.getTransaction().commit();
			T e = (T)o;
			e.removePropertyChangeListener(this);
			this.setChanged();
			ModelParamObserver p = new ModelParamObserver(ModelParamObserver.REMOVE_SINGLE,oldindex);
			this.notifyObservers(p);
		}
		else
			if(dataBaseLink) session.getTransaction().rollback();
		return b;
	}

	synchronized public boolean removeAll(Collection<?> c) {

		if(dataBaseLink)
		{
			session.beginTransaction();
			for (Object o : c)
			{
				session.delete(o);
			}
		}
		boolean b = data.removeAll(c);
		if(b)
		{
			if(dataBaseLink)
				session.getTransaction().commit();
			for(Object e : c)
			{
				T e1 = (T)e;
				e1.removePropertyChangeListener(this);
			}
			this.setChanged();
			ModelParamObserver p = new ModelParamObserver(ModelParamObserver.REMOVE_MULTIPLE,0,this.size()-1);
			this.notifyObservers(p);
		}
		else
			if(dataBaseLink) session.getTransaction().rollback();
		
		return b;
	}

	synchronized public boolean retainAll(Collection<?> c) {
		boolean b =  data.retainAll(c);
		this.setChanged();
		ModelParamObserver p = new ModelParamObserver(ModelParamObserver.REMOVE_MULTIPLE,0,this.size()-1);
		this.notifyObservers(p);
		return b;
	}

	public T set(int index, T element) {
		session.beginTransaction();
		session.saveOrUpdate(element);
		session.getTransaction().commit();
		T e =  data.set(index, element);
		e.removePropertyChangeListener(this);
		element.addPropertyChangeListener(this);
		this.setChanged();
		ModelParamObserver p = new ModelParamObserver(ModelParamObserver.CHANGE_SINGLE,index);
		this.notifyObservers();
		return e;
	}

	public int size() {
		return data.size();
	}

	public List<T> subList(int fromIndex, int toIndex) {
		return data.subList(fromIndex, toIndex);
	}

	public Object[] toArray() {
		return data.toArray();
	}

	public <T> T[] toArray(T[] a) {
		return data.toArray(a);
	}

	public void propertyChange(PropertyChangeEvent arg0) {
		if(dataBaseLink)
		{
			T e = (T) arg0.getSource();
			session.beginTransaction();
			session.saveOrUpdate(e);
			session.getTransaction().commit();
		}
		this.setChanged();
		this.notifyObservers();
	}
}
